package com.xh.common.thread.ext.executor;

import com.xh.common.thread.ext.queue.LevelBlockingQueue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class LevelWorker implements Runnable {

    private final Thread mThread;
    private Command mFirstTask;

    private LevelThreadPoolExecutor mOuter;
    private LevelInternalExecutor mInner;

    private volatile long mCompletedTasks;

    public LevelWorker(LevelThreadPoolExecutor executor, LevelInternalExecutor internal, Command task) {
        mOuter = executor;
        mInner = internal;

        mFirstTask = task;
        mThread = mOuter.getThreadFactory().newThread(this);
    }

    Thread getThread() {
        return mThread;
    }

    public long getCompletedTasks() {
        return mCompletedTasks;
    }

    @Override
    public void run() {
        runWorker(this);
    }

    private void runWorker(LevelWorker w) {
        Thread wt = Thread.currentThread();
        Command task = w.mFirstTask;
        w.mFirstTask = null;

        boolean completedAbruptly = true;


        try {
            while (task != null || (task = getTask()) != null) {

                try {
                    LevelExecutorDetector detector = mOuter.mDetector;

                    if (detector != null) {
                        detector.beforeExecute(task, mInner.getLevel(), wt);
                    }

                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        if (detector != null) {
                            detector.afterExecute(task, mInner.getLevel(), thrown);
                        }
                    }
                } finally {
                    task = null;
                    mCompletedTasks++;
                }
            }
            completedAbruptly = false;
        } finally {
            mInner.processWorkerExit(w, completedAbruptly);
        }
    }

    private Command getTask() {
        LevelConfig config = mInner.getConfig();
        AtomicInteger workCount = mInner.getWorkCount();
        LevelBlockingQueue<Command> workQueue = mOuter.mWorkQueue;

        boolean timedOut = false;

        for (;;) {

            int wc = workCount.get();
            int rs = mOuter.getStatus();

            // Check if queue empty only if necessary.
            if (rs >= LevelThreadPoolExecutor.SHUTDOWNED && (rs >= LevelThreadPoolExecutor.STOP || workQueue.isEmpty())) {
                workCount.decrementAndGet();
                return null;
            }

            boolean timed = config.mAllowCoreThreadTimeOut || wc > config.mCorePoolSize;

            if ((wc > config.mMaximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (workCount.compareAndSet(wc, wc - 1)) {
                    return null;
                }
                continue;
            }

            try {
                Command r = timed ?
                        workQueue.poll(mInner.getLevel(), config.mKeepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take(mInner.getLevel());
                if (r != null) {
                    return r;
                }
                timedOut = true;
            } catch (InterruptedException e) {
                timedOut = false;
            }
        }
    }
}